﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ImageTagToolViewModel.ViewModels;
using System.Windows.Input;
using ImageTagToolViewModel.Serialization;

namespace ImageTagToolViewModel.Models
{
    /// <summary>
    /// This model handles the methods concerning tags.
    /// </summary>
    public class TagProcessingModel
    {
        private MainWindowViewModel _MainWindow;
        /// <summary>
        /// The actually selected tag-group on the view.
        /// </summary>
        private string _SelectedKey;
        private string _PreviousKey;

        /// <summary>
        /// An own reference to the ActualPicture.
        /// </summary>
        private Picture _ActualPicture;

        /// <summary>
        /// Creates a new instance of the TagProcessingModel.
        /// </summary>
        public TagProcessingModel(MainWindowViewModel mainWindow)
        {
            _MainWindow = mainWindow;
            _SelectedKey = TagDatabase.SceneTags;
            _PreviousKey = TagDatabase.SceneTags;
            // This component is notified by an event, when the
            // PictureHandler.Instance.ActualPicture becomes available.
            mainWindow.ImageProcessingModel.ActualPictureAppeared += (actualPicture) =>
                {
                    _ActualPicture = actualPicture;
                };
        }

        /// <summary>
        /// Refreshes the DB tags' list according to the given text.
        /// </summary>
        internal void TagTextBoxTextChanged(string text)
        {
            if (string.IsNullOrEmpty(text))
            {
                RefillDBTagsList();
                return;
            }

            List<string> cache;
            if (text.StartsWith("*"))
                cache = TagDatabase.Instance.GetCollection(_SelectedKey).FindAll(s => s.Contains(text.Substring(1)));
            else
                cache = TagDatabase.Instance.GetCollection(_SelectedKey).FindAll(s => s.StartsWith(text));

            _MainWindow.DBTagsList.Clear();
            foreach (string s in cache)
                _MainWindow.DBTagsList.Add(s);
        }

        public void TagTextBox_PreviewKeyDown(Key key)
        {
            if (key == Key.Return)
            {
                // If the user pressed Enter.
                if (_MainWindow.DBTagsList.Count == 1)
                {
                    // If there is only one relevant result.
                    string selectedItem = _MainWindow.DBTagsList[0];
                    if (!_MainWindow.ImageTagsList.Contains(selectedItem))
                    {
                        // Addig to database.
                        TagDatabase.Instance.GetCollection(_SelectedKey).Add(selectedItem);
                        // Adding tag.
                        _MainWindow.ImageTagsList.Add(selectedItem);
                    }
                }
                else if (_MainWindow.DBTagsList.Count == 0)
                {
                    // If there is no hit, the textbox's content will be added.
                    if (!string.IsNullOrEmpty(_MainWindow.TagTextBoxText))
                    {
                        TagDatabase.Instance.GetCollection(_SelectedKey).Add(_MainWindow.TagTextBoxText);
                        // Adding tag.
                        _MainWindow.ImageTagsList.Add(_MainWindow.TagTextBoxText);
                        _MainWindow.DBTagsList.Add(_MainWindow.TagTextBoxText);
                    }
                }

                // Refilling DB-tags' listbox.
                RefillDBTagsList();

                // Clearing the tag-textbox.
                _MainWindow.ClearTagTextBox();
            }
        }

        /// <summary>
        /// Make all the DB-tags' list visible on the UI.
        /// </summary>
        internal void RefillDBTagsList()
        {
            _MainWindow.DBTagsList.Clear();
            foreach (string s in TagDatabase.Instance.GetCollection(_SelectedKey).Entries)
                _MainWindow.DBTagsList.Add(s);
        }

        /// <summary>
        /// Saves changes made to the current actual picture.
        /// </summary>
        internal void SaveState()
        {
            if (_ActualPicture != null)
            {
                // Saving state before changing to the actual key.
                // If the previuous key was the invalid key, then no action is needed here.
                switch (_PreviousKey)
                {
                    case TagDatabase.SceneTags:
                        _ActualPicture.SceneTags.Clear();
                        _ActualPicture.SceneTags.AddRange(_MainWindow.ImageTagsList);
                        break;
                    case TagDatabase.PersonTags:
                        _ActualPicture.PersonTags.Clear();
                        _ActualPicture.PersonTags.AddRange(_MainWindow.ImageTagsList);
                        break;
                    case TagDatabase.OtherTags:
                        _ActualPicture.OtherTags.Clear();
                        _ActualPicture.OtherTags.AddRange(_MainWindow.ImageTagsList);
                        break;
                }
            }
            

            ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // This functionality has been removed, because it is very resource hungry.
            // The saving will happen, when the application is being closed.
            //
            //PicturesXml pictures = new PicturesXml();
            //PictureHandler.Instance.Pictures.ForEach(x => pictures.pictureList.Add(x));
            //pictures.LocationList.AddRange(LocationDatabase.Instance.ScannedLocations);
            //SerializationProvider.SerializePictures("pictures.xml", pictures);
        }

        /// <summary>
        /// Deletes the specified tag from the image.
        /// </summary>
        public void RemoveTagFromImage(string tag)
        {
            PictureHandler.Instance.ActualPicture.SceneTags.Remove(tag);
            _MainWindow.ImageTagsList.Remove(tag);
            TagDatabase.Instance.GetCollection(_SelectedKey).DecreaseReferencesOf(tag);
        }

        /// <summary>
        /// Deletes the specified tag from the database.
        /// </summary>
        public void RemoveTagFromDatabase(string tag)
        {
            int references = TagDatabase.Instance.GetCollection(_SelectedKey).GetReferencesOf(tag);
            if (references == 0)
            {
                TagDatabase.Instance.GetCollection(_SelectedKey).Remove(tag);
                RefillDBTagsList();
            }
            else
                throw new Exception("The tag cannot be removed, because it is used elsewhere.");
        }

        /// <summary>
        /// Changes the selected tag-group.
        /// </summary>
        public void SetSelectedKey(string key)
        {
            if (key == TagDatabase.SceneTags || key == TagDatabase.PersonTags || key == TagDatabase.OtherTags)
            {
                // If the provided key is valid:
                _PreviousKey = _SelectedKey;
                _SelectedKey = key;
                SaveState();
                FillTagsListBox();
                RefillDBTagsList();
            }
            else
                throw new InvalidOperationException("The provided key is not valid. Key: " + key);
        }

        /// <summary>
        /// Shows the actual picture's tags according to the selected tag-group.
        /// </summary>
        internal void FillTagsListBox()
        {
            // If there's no change:
            if (_PreviousKey == _SelectedKey)
                return;

            // New state.
            _MainWindow.ImageTagsList.Clear();
            List<string> tags = new List<string>();
            switch (_SelectedKey)
            {
                case TagDatabase.SceneTags:
                    tags = _ActualPicture.SceneTags;
                    break;
                case TagDatabase.PersonTags:
                    tags = _ActualPicture.PersonTags;
                    break;
                case TagDatabase.OtherTags:
                    tags = _ActualPicture.OtherTags;
                    break;
            }
            foreach (string tag in tags)
                _MainWindow.ImageTagsList.Add(tag);

            _PreviousKey = _SelectedKey;
        }

        /// <summary>
        /// Refreshes the model, when changing to new picture.
        /// </summary>
        public void NewPicture()
        {
            // Saving the old picture's state.
            SaveState();
            _PreviousKey = TagDatabase.InvalidTagKey;

            // Setting up the new picture.
            _ActualPicture = PictureHandler.Instance.ActualPicture;
            FillTagsListBox();
        }
    }
}
